#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>
#include <errno.h>
#include <time.h>

#include "ring_fifo.h"

//编译
//gcc -Wall main.c ring_fifo.c -lpthread
//对比
//cmp read_file_len write_file_len
//cmp read_file write_file

pthread_t thd_rd[1],thd_wr[1];

pthread_mutex_t thd_rd_write_file_mutex;
pthread_mutex_t thd_wr_write_file_mutex;

static void* task_read(void *para);
static void* task_write(void *para);
static uint32_t get_tick_count(void);

struct ring_fifo_t *ringBuf_handle;
//必须是2的幂次方
uint8_t buf[1u << 17] = {0};

#define TEST_DATA_SIZE 2048
uint8_t test_write_data[TEST_DATA_SIZE] = {0},test_read_data[TEST_DATA_SIZE] = {0};

//写次数
#define WR_TIMERS (rand() % (1000000 - 500000 + 1) + 500000)

//待测试的fifo类型
#define TEST_FIFO_TYPE RF_TYPE_FRAME//RF_TYPE_STREAM

sem_t sem;

FILE *f_write,*f_read;
FILE *f_write_len,*f_read_len;

volatile uint32_t wr_thread_is_runing = 1;

int main(int argc,char *argv[])
{
    uint32_t status;
    int ret;

    if(sizeof(thd_wr) / sizeof(*thd_wr) > 1)
    {
        pthread_mutex_init(&thd_wr_write_file_mutex,NULL);
    }

    if(sizeof(thd_rd) / sizeof(*thd_rd) > 1)
    {
        pthread_mutex_init(&thd_rd_write_file_mutex,NULL);
    }

    ret = sem_init(&sem,0,0);
    if(ret < 0)
    {
        perror("sem_init");
    }

    f_write = fopen("tmpfile/write_file","w+");
    if(!f_write)
    {
        perror("fopen write_file");
    }
    f_read = fopen("tmpfile/read_file","w+");
    if(!f_read)
    {
        perror("fopen read_file");
    }

    f_write_len = fopen("tmpfile/write_file_len","w+");
    if(!f_write_len)
    {
        perror("fopen write_file_len");
    }
    f_read_len = fopen("tmpfile/read_file_len","w+");
    if(!f_read_len)
    {
        perror("fopen read_file_len");
    }

    status = ring_fifo_init(&ringBuf_handle,NULL,sizeof(buf),TEST_FIFO_TYPE);
    if(status != RF_SUCCESS)
    {
        printf("%s failed error:%d\r\n",__FUNCTION__,status);
    }

    for(uint32_t i = 0;i < sizeof(test_write_data);i++)
    {
        test_write_data[i] = rand() % (0x39 - 0x30 + 1) + 0x30;
        usleep(1000);
    }

    for(uint32_t i = 0;i < sizeof(thd_rd) / sizeof(*thd_rd);i++)
    {
        pthread_create(&thd_rd[i],NULL,task_read,NULL);
    }
    for(uint32_t i = 0;i < sizeof(thd_wr) / sizeof(*thd_wr);i++)
    {
        pthread_create(&thd_wr[i],NULL,task_write,NULL);
    }

    for(uint32_t i = 0;i < sizeof(thd_wr) / sizeof(*thd_wr);i++)
    {
        pthread_join(thd_wr[i],NULL);
    }
    wr_thread_is_runing = 0;
    for(uint32_t i = 0;i < sizeof(thd_rd) / sizeof(*thd_rd);i++)
    {
        pthread_join(thd_rd[i],NULL);
    }

    ring_fifo_destroy(ringBuf_handle);
    sem_destroy(&sem);
    fclose(f_write);
    fclose(f_read);
    fclose(f_write_len);
    fclose(f_read_len);

    return 0;
}

void* task_read(void *para)
{
    uint32_t status;
    int ret;
    struct timespec timeout = {0};
    size_t size;
    uint16_t rd_len;
    char tmpStr[32] = {0};
    static uint32_t read_failed_times = 0,read_success_times = 0;

    //计算相对时间
    clock_gettime(CLOCK_REALTIME,&timeout);
    timeout.tv_sec += 1;

    for(;;)
    {
        ret = sem_timedwait(&sem,&timeout);
        if(ret < 0)
        {
            if((errno == EINTR) || (errno == EAGAIN))
            {
                continue;
            }
            else if(errno = ETIMEDOUT)
            {
                if(!wr_thread_is_runing)
                {
                    break;
                }
                else
                {
                    continue;
                }
            }
            else
            {
                perror("sem_timedwait");
            }
        }

        if(sizeof(thd_rd) / sizeof(*thd_rd) > 1)
        {
            pthread_mutex_lock(&thd_rd_write_file_mutex);
        }

        status = ring_fifo_read(ringBuf_handle,test_read_data,sizeof(test_read_data),&rd_len);
        if(status != RF_SUCCESS)
        {
            ++read_failed_times;
            if(status != RF_EMPTY)
                printf("%s failed error:%d\r\n",__FUNCTION__,status);
        }
        else
        {
            ++read_success_times;

            size = fwrite(test_read_data,rd_len,1,f_read);
            if(size < 1)
            {
                printf("thread[%lu] read write file failed..\r\n",pthread_self());
            }
            else
            {
                sprintf(tmpStr,"%u\t",rd_len);
                if(!(read_success_times % 32))
                {
                    sprintf(tmpStr + strlen(tmpStr),"\n");
                }
                size = fwrite(tmpStr,strlen(tmpStr),1,f_read_len);
                if(size < 1)
                {
                    printf("thread[%lu] read write len to file failed..\r\n",pthread_self());
                }
//                printf("read:%s\t",tmpStr);
            }
        }

        if(sizeof(thd_rd) / sizeof(*thd_rd) > 1)
        {
            pthread_mutex_unlock(&thd_rd_write_file_mutex);
        }

        //模拟数据处理
        // usleep(100);
    }

    printf("\r\nthread[%lu] read_failed_times:%u,read_success_times:%u,total:%u\r\n",pthread_self(),read_failed_times,read_success_times,read_failed_times + read_success_times);
    printf("thread[%lu] task_read exit..\r\n",pthread_self());

    return (void *)0;
}

void* task_write(void *para)
{
    uint32_t status;
    size_t size;
    uint32_t wr_len = 0;
    char tmpStr[32] = {0};
    static uint32_t write_failed_times = 0,write_success_times = 0;

    srand(get_tick_count());
    for(uint32_t i = 0;i < WR_TIMERS;i++)
    {
        if(sizeof(thd_wr) / sizeof(*thd_wr) > 1)
        {
            pthread_mutex_lock(&thd_wr_write_file_mutex);
        }

        srand(get_tick_count());
        wr_len = (rand() % TEST_DATA_SIZE) + 1;
        status = ring_fifo_write(ringBuf_handle,test_write_data,wr_len);
        if(status != RF_SUCCESS)
        {
            ++write_failed_times;
            if(status != RF_FULL)
                printf("%s failed error:%d\r\n",__FUNCTION__,status);
        }
        else
        {
            ++write_success_times;

            size = fwrite(test_write_data,wr_len,1,f_write);
            if(size < 1)
            {
                printf("thread[%lu] write write file failed..\r\n",pthread_self());
            }
            else
            {
                sprintf(tmpStr,"%u\t",wr_len);
                if(!(write_success_times % 32))
                {
                    sprintf(tmpStr + strlen(tmpStr),"\n");
                }
                size = fwrite(tmpStr,strlen(tmpStr),1,f_write_len);
                if(size < 1)
                {
                    printf("thread[%lu] write write len to file failed..\r\n",pthread_self());
                }
//                printf("write:%s\t",tmpStr);
            }
            sem_post(&sem);
        }

        if(sizeof(thd_wr) / sizeof(*thd_wr) > 1)
        {
            pthread_mutex_unlock(&thd_wr_write_file_mutex);
        }

        //模拟数据接收
        usleep(1);
    }

    printf("\r\nthread[%lu] write_failed_times:%u,write_success_times:%u,total:%u\r\n",pthread_self(),write_failed_times,write_success_times,write_failed_times + write_success_times);
    printf("thread[%lu] task_write exit..\r\n",pthread_self());

    return (void *)0;
}

uint32_t get_tick_count(void)
{
    struct timespec tp;

    clock_gettime(CLOCK_MONOTONIC,&tp);

    return tp.tv_nsec;
}
